package com.weic.easycache.framework;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import android.content.Context;
import android.util.Log;

import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.dao.Dao;
import com.weic.easycache.Debug;
import com.weic.easycache.framework.DataManager.Callback;
import com.weic.easycache.framework.biz.AbsBaseBiz;
import com.weic.easycache.framework.config.TaskConfig;
import com.weic.easycache.framework.db.ApiCacheTable;
import com.weic.easycache.task.ApiTask;
import com.weic.easycache.task.DBTask;
import com.weic.easycache.task.ImgDlodTask;
import com.weic.easycache.task.TaskQueue;
import com.weic.easycache.task.TaskQueue.Task;
import com.weic.easycache.task.TaskQueue.TaskBuilder;
import com.weic.easycache.task.TaskQueue.TaskListener;
import com.weic.easycache.utils.ReflectUtils;

//TODO 增加多监听支持和DataManager弱引用
public class Proxy<P extends Param, D> {
	private static final String TAG = Proxy.class.getSimpleName();

	private final AtomicInteger mCount = new AtomicInteger(0);

	private Context mContext;
	private DataManager mDManager;
	private String mKey;
	private Request mRequest;
	private P mParam;
	
	private volatile Callback<D> mCallback;
	private volatile AbsBaseBiz<P, D> mBiz;

	private volatile boolean mLoading = false;

	/*只有在除callback以外的所有参数都没有变的情况下，才能找到缓存的本Proxy，此时context也是没有变的*/
	public Proxy(Context context, DataManager dManager, String key, Request request, P param, Callback<D> callback) {
		mContext = context;
		mDManager = dManager;
		mKey = key;
		mRequest = request;
		mParam = param;
		mCallback = callback;
	}
	
	void update(Callback<D> callback) {
		mCallback = callback;
	}

	private OrmLiteSqliteOpenHelper getHelper() {
		mCount.incrementAndGet();
		if(Debug.LOG) Log.d(TAG, toString() + " [release] 引用数：" + mCount.get());
		return mDManager.getDBHelper();
	}

	private void releaseHelper() {
		while(mCount.getAndDecrement() > 0) {
			if(Debug.LOG) Log.d(TAG, toString() + " [release] 引用数：" + mCount.get());
			mDManager.releaseDBHelper();
		}
	}

	@SuppressWarnings("unchecked")
	public void loadData() {
		if(mLoading) return;
		mLoading = true;

		TaskBuilder taskBuilder = TaskQueue.builder();
		try {
			mBiz = (AbsBaseBiz<P, D>) mRequest.biz.newInstance();
			mBiz.setParam(mParam);
			Dao<ApiCacheTable, String> dao = null;
			for(TaskConfig<?, ?, ?> task : mRequest.tasks) {
				if(task.type == TaskConfig.TYPE_TABLE) {
					taskBuilder.queueTask(new DBTask(mContext, task.id, task.table.getDao(getHelper()), task.writable, mBiz));
				}else if(task.type == TaskConfig.TYPE_API) {
					Config.get().checkApiConfig(task.api);
					if(dao == null) dao = getHelper().getDao(ApiCacheTable.class);
					taskBuilder.queueTask(new ApiTask(mContext, task.id, mBiz, task.canIrgnore, task.api,
							task.api.getMappedParams(convertParamToMap()), dao, task.lastUsed, mParam.refresh, task.needful));
				}else if(task.type == TaskConfig.TYPE_IMG) {
					ImgParam imgpa = (ImgParam)mParam;
					taskBuilder.queueTask(new ImgDlodTask(mContext, task.id, imgpa.imgUrls,
							imgpa.imgCategory, mBiz, task.toBitmap, task.irgnoreCache, task.needful, task.canIrgnore));
				}
			}
			taskBuilder.setTaskListener(mTaskListener);
			mDManager.postWaitingLoading(mCallback, false, null);
			taskBuilder.scheduleTasks();
		} catch (SQLException e) {
			releaseHelper();
			mDManager.postFailure(mCallback, 508, 1, "数据库异常：" + e.getLocalizedMessage());
			mLoading = false;
			e.printStackTrace();
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			releaseHelper();
			mDManager.postFailure(mCallback, 508, 2, "未知错误：" + e.getLocalizedMessage());
			mLoading = false;
			e.printStackTrace();
		}
	}

	private TaskListener mTaskListener = new TaskListener() {
		@Override
		public void onStart() {
			mDManager.postWaitingLoading(mCallback, true, null);
		}

		@Override
		public void onComplete(boolean hasBgTask) {
			if(!hasBgTask) releaseHelper();
			mDManager.postComplete(mCallback, mBiz.getResult(), mKey, mRequest.cache, !hasBgTask);
			mLoading = hasBgTask;
		}

		@Override
		public void onUpdate() {
			releaseHelper();
			mDManager.postUpdate(mCallback, mBiz.getResult(), mKey, mRequest.cache);
			mLoading = false;
		}

		@Override
		public void onProgress(int taskId, float progress, String message, int errorType, int errorCode) {
			mDManager.postProgress(mCallback, mParam.deliverDataOnProgress ? mBiz.getResult() : null, taskId, progress, message, resolveErrorCode(errorType), resolveErrorCode(errorCode));
		}

		@Override
		public void onFailure(int errorType, int errorCode, String message) {
			releaseHelper();
			mDManager.postFailure(mCallback, resolveErrorCode(errorType), resolveErrorCode(errorCode), message);
			mLoading = false;
		}
	};
	
	private int resolveErrorCode(int code) {
		switch (code) {
		case Task.ERROR_NO:
			code = Errors.NO;
			break;
		case Task.ERROR_UNKNOWN:
			code = Errors.UNKNOWN;
			break;
		}
		return code;
	}

	Map<String, String> mParamsMap;
	private Map<String, String> convertParamToMap() {
		if(mParamsMap == null) {
			mParamsMap = new HashMap<String, String>();
			P param = mParam;
			Field[] fields = param.getClass().getFields();
			String value;
			for(Field field : fields) {
				value = ReflectUtils.getBasicFieldValue(field, param);
				//只有基本类型和简单类型才可作为参数值，一些大对象在读取value的时候已经过滤，因此可能为null
				if(value != null) mParamsMap.put(field.getName().toLowerCase(), value);
			}
		}
		return mParamsMap;
	}
}
